using System;
using System.Collections.Generic;

using Microsoft.Cci;
using Microsoft.FxCop.Sdk;
using Microsoft.FxCop.Sdk.Introspection;

namespace Tyng.Rules
{
	public static class InstructionUtilities
	{
        public static bool IsUsageOfVariable(Variable variable, Instruction instruction)
		{
			if (IsUsageInstruction(instruction) && ((Variable) instruction.Value == variable))
				return true;

			return false;
		}

		public static bool IsUsageInstruction(Instruction instruction)
		{
			switch (instruction.OpCode)
			{
				case OpCode.Ldloc_0:
				case OpCode.Ldloc_1:
				case OpCode.Ldloc_2:
				case OpCode.Ldloc_3:
				case OpCode.Ldloc_S:
				case OpCode.Ldloca_S:
				case OpCode.Ldloc:
				case OpCode.Ldloca:
				{
					return true;
				}
			}
			return false;

		}

		public static bool FindTryCatchFinally(Method method, int instructionIndex, out int start, out int end)
		{
			int dummy = -1;

			bool inCatch = false;
			bool inFinally = false;
			bool inTry = FindBlocksContainingInstruction(method, instructionIndex, true, false, false, OpCode._Try, OpCode._EndTry, out start, out end);

			if(! inTry)
			{
				inCatch = FindBlocksContainingInstruction(method, instructionIndex, true, false, false, OpCode._Catch, OpCode._EndHandler, out start, out end);

				if(! inCatch)
				{
					inFinally = FindBlocksContainingInstruction(method, instructionIndex, true, false, false, OpCode._Finally, OpCode.Endfinally, out start, out end);

					if(! inFinally)
						return false;
				}
			}

			//Go to beginning of catches
			if(inFinally)
			{
				while(method.Instructions[start - 1].OpCode == OpCode._EndHandler)
				{
					if(! FindBlocksContainingInstruction(method, start - 1, true, false, true, OpCode._Catch, OpCode._EndHandler, out start, out dummy))
						throw new ApplicationException("Invalid IL");
				}
				
				if(method.Instructions[start - 1].OpCode != OpCode._EndTry)
					throw new ApplicationException("Invalid IL - InFinally - Catches");
			}

			//go to beginning of try
			if(inFinally || inCatch)
			{
				if(FindBlocksContainingInstruction(method, start - 1, true, false, true, OpCode._Try, OpCode._EndTry, out start, out dummy))
					if(inFinally)
						return true;
				else
					throw new ApplicationException("Invalid IL - InFinally / InCatch - Try");
			}

			//go to end of catches
			if(inTry)
			{
				while(method.Instructions[end + 1].OpCode == OpCode._Catch)
				{
					if(! FindBlocksContainingInstruction(method, end + 1, true, true, false, OpCode._Catch, OpCode._EndHandler, out dummy, out end))
						throw new ApplicationException("Invalid IL - InTry - Catches");
				}
			}

			//go to end of finally
			if(inTry || inCatch)
			{
				if(method.Instructions[end + 1].OpCode == OpCode._Finally)
					FindBlocksContainingInstruction(method, end + 1, true, true, false, OpCode._Finally, OpCode.Endfinally, out dummy, out end);
			}

            return true;				
		}

		public static bool FindBlocksContainingInstruction(Method method, int instructionIndex, OpCode before, OpCode after)
		{
			return FindBlocksContainingInstruction(method, instructionIndex, false, false, before, after);
		}

		public static bool FindBlocksContainingInstruction(Method method, int instructionIndex, bool includeInstructionInBeforeSearch, bool includeInstructionInAfterSearch, OpCode before, OpCode after)
		{
			return FindBlocksContainingInstruction(method, instructionIndex, true, includeInstructionInBeforeSearch, includeInstructionInAfterSearch, before, after);
		}

		public static bool FindBlocksContainingInstruction(Method method, int instructionIndex, bool nestingCheck, bool includeInstructionInBeforeSearch, bool includeInstructionInAfterSearch, OpCode before, OpCode after)
		{
			int dummy1, dummy2;

			return FindBlocksContainingInstruction(method, instructionIndex, nestingCheck, includeInstructionInBeforeSearch, includeInstructionInAfterSearch, before, after, out dummy1, out dummy2);
		}

		public static bool FindBlocksContainingInstruction(Method method, int instructionIndex, bool nestingCheck, bool includeInstructionInBeforeSearch, bool includeInstructionInAfterSearch, OpCode before, OpCode after, out int start, out int end)
		{
			start = -1;
			end = -1;

			InstructionList instructions = method.Instructions;

			int searchStart = instructionIndex;

			if(! includeInstructionInBeforeSearch)
				searchStart --;

			for(int i = searchStart; i >= 0; i--)
			{
				if(instructions[i].OpCode == after)
				{
					if(! nestingCheck)
						return false;

					int childBlockStart, childBlockEnd;

					if(FindBlocksContainingInstruction(method, i, true, false, true, before, after, out childBlockStart, out childBlockEnd))
					{
						i = childBlockStart;
					}
					else
						return false;
				}
				else if(instructions[i].OpCode == before)
				{
					start = i;
					break;
				}
			}

			if(start == -1)
				return false;

			searchStart = instructionIndex;

			if(! includeInstructionInAfterSearch)
				searchStart ++;

			for(int i = searchStart; i < instructions.Length; i++)
			{
				if(instructions[i].OpCode == before)
				{
					if(! nestingCheck)
						return false;

					int childBlockStart, childBlockEnd;

					if(FindBlocksContainingInstruction(method, i, false, false, true, before, after, out childBlockStart, out childBlockEnd))
					{
						i = childBlockEnd;
					}
					else
						return false;
				}
				else if(instructions[i].OpCode == after)
				{
					end = i;
					return true;
				}
			}

			return false;
		}
	}
}
